<template>
  <ContentWrap :bodyStyle="{ padding: '0px', borderRadius: '12px' }" class="!mb-0">
    <div
      class="w-full h-[calc(100vh-var(--top-tool-height)-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-2px)]"
    >
      <div class="task-edit">
        <div class="task-header">
          <el-form class="task-form" :model="taskData" ref="queryFormRef" label-width="130px">
            <el-row :gutter="10">
              <el-col :span="6">
                <el-form-item label="任务名称" prop="name" style="margin-bottom: 0">
                  <el-input
                    disabled
                    v-model="taskData.name"
                    placeholder="请输入任务名称"
                    clearable
                  />
                </el-form-item>
              </el-col>
              <el-col :span="6">
                <el-form-item label="任务描述" prop="remark" style="margin-bottom: 0">
                  <el-input
                    disabled
                    v-model="taskData.remark"
                    placeholder="请输入任务描述"
                    clearable
                  />
                </el-form-item>
              </el-col>
              <el-col :span="6">
                <el-form-item label="被监测设备" prop="monitorDeviceId" style="margin-bottom: 0">
                  <el-select
                    disabled
                    v-model="taskData.monitorDeviceId"
                    clearable
                    placeholder="请选择被监测设备"
                  >
                    <el-option
                      v-for="item in monitorDeviceList"
                      :key="item.id"
                      :label="item.deviceName"
                      :value="item.id"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="6">
                <el-form-item
                  label="是否启用报警规则"
                  prop="state"
                  style="margin-bottom: 0"
                  label-width="130px"
                >
                  <el-select
                    disabled
                    v-model="taskData.state"
                    placeholder="请选择是否启用报警规则"
                    clearable
                    class="!w-240px"
                  >
                    <el-option
                      v-for="dict in getIntDictOptions(DICT_TYPE.VMIOT_TASK_STATE)"
                      :key="dict.value"
                      :label="dict.label"
                      :value="dict.value"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>

          <div class="operater-group">
            <el-button v-if="query.id" type="default" @click="openAlarmRuleForm"
              >报警规则配置</el-button
            >
            <el-button type="primary" @click="saveScene">保存</el-button>
          </div>
        </div>

        <div class="task-body">
          <div class="task-running-state">
            <el-tag v-if="taskRunningState === 1" size="large" type="success" effect="dark">
              运行中
            </el-tag>
            <el-tag v-if="taskRunningState === 0" size="large" type="danger" effect="dark">
              未运行
            </el-tag>
          </div>
          <div class="task-sider">
            <div class="sensor-item" :draggable="true" @dragstart="onDragStart($event, 'input')">
              <img />
              物理传感器
            </div>
            <div class="sau-item" :draggable="true" @dragstart="onDragStart($event, 'default')">
              SAU
            </div>
            <div class="measure-item" :draggable="true" @dragstart="onDragStart($event, 'output')">
              测点
            </div>
          </div>
          <div class="task-content" @drop="onDrop">
            <VueFlow
              v-if="isInit"
              :multiSelectionKeyCode="[]"
              :nodes="taskData.content.nodes"
              :edges="taskData.content.edges"
              :apply-default="false"
              :delete-key-code="['Backspace', 'Delete']"
              @dragover="onDragOver"
              @dragleave="onDragLeave"
              auto-connect
            >
              <DropzoneBackground
                :style="{
                  backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
                  transition: 'background-color 0.2s ease'
                }"
              />
              <template #node-input="nodeProps">
                <physical-sensors
                  :taskHook="taskHook"
                  :node="nodeProps"
                  @sensor-data-change="handleSensorDataChange"
                />
              </template>

              <template #node-default="nodeProps">
                <SAU
                  :ref="(el: any) => setSauRef(nodeProps.id, el)"
                  :taskHook="taskHook"
                  :node="nodeProps"
                  @sau-change="submitData"
                />
              </template>

              <template #node-output="nodeProps">
                <Measure :taskHook="taskHook" :node="nodeProps" />
              </template>

              <template #connection-line="{ sourceX, sourceY, targetX, targetY }">
                <path
                  class="vue-flow__connection animated"
                  fill="none"
                  stroke="#5C62FF"
                  :stroke-width="2.5"
                  :d="`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`"
                />

                <circle
                  :cx="targetX"
                  :cy="targetY"
                  fill="#fff"
                  :r="4"
                  stroke="#6F3381"
                  :stroke-width="1.5"
                />
              </template>
            </VueFlow>
          </div>
        </div>
      </div>
    </div>
    <AlarmRuleForm v-if="isInit" :taskData="taskData" ref="alarmRuleFormRef" />
  </ContentWrap>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import useDragAndDrop from './useDnD'
import DropzoneBackground from './DropzoneBackground.vue'
import { VueFlow, useVueFlow, MarkerType } from '@vue-flow/core'
import PhysicalSensors from './PhysicalSensors.vue'
import SAU from './SAU.vue'
import Measure from './Measure.vue'
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'

import { TaskApi } from '@/api/vmiot/task'
import { useTask } from './useTask'
import AlarmRuleForm from '../alarmrule/AlarmRuleForm.vue'
const { query } = useRoute() // 查询参数
const { push } = useRouter()

const alarmRuleFormRef = ref<InstanceType<typeof AlarmRuleForm>>()

const {
  toObject,
  onNodesChange,
  applyNodeChanges,
  getConnectedEdges,
  onEdgesChange,
  applyEdgeChanges,
  edges,
  getSelectedEdges
} = useVueFlow()
const { onDragStart, onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop()

const taskHook = useTask()
const taskId = ref((query.id as unknown as number) || '')

const {
  taskData,
  monitorDeviceList,
  isInit,
  init,
  isMatchSignalType,
  isExistSensorInSauInstance,
  taskRunningState
} = taskHook

// 用对象保存多个 SAU ref
const sauRefs: Record<string, InstanceType<typeof SAU> | null> = {}

// 注册 ref（支持动态更新）
const setSauRef = (id: string, el: InstanceType<typeof SAU> | null) => {
  sauRefs[id] = el
}

const handleSensorDataChange = () => {
  Object.values(sauRefs).forEach((sauRef) => {
    if (sauRef) {
      sauRef.calcConnectedSensorList()
    }
  })
}

/**
 * 根据传感器节点id获取相连的sau节点数据
 * @param id
 */
const getConnectedNodesData = (id: string) => {
  const { nodes } = { ...toObject() }
  const connectedEdges = getConnectedEdges(id)
  const connectedNodesData = connectedEdges.map((item) => {
    return nodes.find((node) => node.id === item.target)
  })
  return connectedNodesData
}

/**
 * 根据节点id返回节点数据
 * @param id
 */
const getNodeData = (id: string) => {
  const { nodes } = { ...toObject() }
  return nodes.find((node) => node.id === id)
}

onNodesChange(async (changes) => {
  const nextChanges: any[] = []
  for (const change of changes) {
    if (change.type === 'remove') {
      // 删除物理传感器
      if (change.id.includes('sensor')) {
        await ElMessageBox.confirm('确定删除物理传感器？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
        const connectedSauNodes = getConnectedNodesData(change.id)
        if (connectedSauNodes.some((item) => item?.data.sauInstanceId)) {
          ElMessage.error('存在正常的sau实例相连接，无法删除')
        } else {
          nextChanges.push(change)
        }
      } else if (change.id.includes('sau')) {
        const res = await ElMessageBox.confirm('确定删除SAU？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
        if (res === 'confirm') {
          const sauNode: any = getNodeData(change.id)
          if (sauNode.data.sauInstanceId) {
            ElMessage.error('存在正常的sau实例，无法删除')
          } else {
            nextChanges.push(change)
          }
        }
      } else if (change.id.includes('measure')) {
        await ElMessageBox.confirm('确定删除测点？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
        nextChanges.push(change)
      } else {
        nextChanges.push(change)
      }
    } else {
      nextChanges.push(change)
    }
  }

  applyNodeChanges(nextChanges)
  if (nextChanges.some((item) => item.type === 'add' || item.type === 'remove')) {
    Object.values(sauRefs).forEach((sauRef) => {
      if (sauRef) {
        sauRef.calcConnectedSensorList()
      }
    })
  }
})

onEdgesChange(async (changes) => {
  const nextChanges: any[] = []
  for (const change of changes) {
    switch (change.type) {
      case 'add':
        const connection = change.item
        connection.markerEnd = {
          type: MarkerType.Arrow,
          color: '#4C62FF',
          strokeWidth: 1.5
        }
        connection.style = {
          stroke: '#4C62FF',
          strokeWidth: 2.5
        }
        if (connection.sourceHandle === 'sensor_a' && connection.targetHandle === 'sau_a') {
          //物理传感器连接sau
          if (connection.id) {
            const sourceNode = getNodeData(connection.source)
            const targetNode = getNodeData(connection.target)
            //验证物理传感器是否已经选择
            if (!(sourceNode && sourceNode.data.sensorData)) {
              ElMessage.error('请先选择物理传感器')
            } else if (
              !(targetNode?.data.sauData?.sauTemplateId && targetNode?.data.sauData?.computeNodeId)
            ) {
              ElMessage.error('请先完善SAU配置')
            } else if (
              !isMatchSignalType(
                targetNode?.data.sauData.sauTemplateId,
                sourceNode?.data.sensorData.sensorId
              )
            ) {
              ElMessage.error('类型不匹配，无法连接')
            } else if (
              targetNode?.data.sauInstanceId &&
              !isExistSensorInSauInstance(
                targetNode.data.sauInstanceId,
                sourceNode.data.sensorData.sensorId
              )
            ) {
              ElMessage.error('已经生成SAU实例，请先删除实例，再连接')
            } else {
              nextChanges.push(change)
            }
          } else {
            nextChanges.push(change)
          }
        } else if (connection.sourceHandle === 'sau_b' && connection.targetHandle === 'measure_b') {
          nextChanges.push(change)
        }
        break
      case 'remove':
        if (getSelectedEdges.value.length > 0) {
          if (change.sourceHandle === 'sensor_a' && change.targetHandle === 'sau_a') {
            const targetNode = getNodeData(change.target)
            if (targetNode?.data.sauInstanceId) {
              ElMessage.error('存在正常的sau实例，无法删除')
            } else {
              nextChanges.push(change)
            }
          } else if (change.sourceHandle === 'sau_b' && change.targetHandle === 'measure_b') {
            nextChanges.push(change)
          } else {
            nextChanges.push(change)
          }
        } else {
          // nextChanges.push(change)
        }

        break
      case 'select':
        const edgeItem: any = edges.value.find((e) => e.id === change.id)
        if (change.selected) {
          edgeItem.markerEnd = {
            type: MarkerType.Arrow,
            color: '#1a40cb',
            strokeWidth: 2.2
          }
          edgeItem.style = {
            stroke: '#1a40cb',
            strokeWidth: 4
          }
        } else {
          edgeItem.markerEnd = {
            type: MarkerType.Arrow,
            color: '#4C62FF',
            strokeWidth: 1.5
          }
          edgeItem.style = {
            stroke: '#4C62FF',
            strokeWidth: 2.5
          }
        }
        nextChanges.push(change)
        break
      default:
        nextChanges.push(change)
        break
    }
  }

  if (nextChanges.length) {
    applyEdgeChanges(nextChanges)
    if (nextChanges.some((item) => item.type === 'add' || item.type === 'remove')) {
      Object.values(sauRefs).forEach((sauRef) => {
        if (sauRef) {
          sauRef.calcConnectedSensorList()
        }
      })
    }
  }
})

/**
 * 提交数据
 */
const submitData = async () => {
  const params: any = {
    ...taskData.value,
    runningState: taskRunningState.value
  }
  params.content = JSON.stringify(toObject())
  await TaskApi.updateTask(params)
}

/**
 * 保存
 */
const saveScene = async () => {
  submitData()
  ElMessage.success('更新成功')
  push('/scene/task')
}

const openAlarmRuleForm = () => {
  taskData.value.content = toObject()
  alarmRuleFormRef.value?.open()
}

onMounted(() => {
  init(taskId.value)
  const route = useRoute()
  route.meta.leaveGuard = () => {
    return false
  }
})

onUnmounted(() => {
  const route = useRoute()
  delete route.meta.leaveGuard
})
</script>
<style lang="scss" scoped>
.el-card {
  border: none;
  border-radius: 12px;
}
.dropzone-background {
  position: relative;
  height: 100%;
  width: 100%;
}
.dropzone-background .overlay {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  pointer-events: none;
}
.task-edit {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 100%;

  .task-header {
    height: 64px;
    padding: 0 16px;
    display: flex;
    align-items: center;
    box-shadow: 0px 3px 2px 1px rgba(51, 51, 51, 0.1);
    border-radius: 12px 12px 0px 0px;

    .task-form {
      flex: 1;
    }

    .operater-group {
      margin-left: 10px;
      width: 190px;
      .el-button--primary {
        background: linear-gradient(90deg, #1a40cb 0%, #5775e7 100%);
        border: 0;
        color: #fff !important;
      }

      .el-button--default {
        border: 1px solid #5473e6;
        color: #5271e5;
      }
    }
  }

  .task-running-state {
    position: absolute;
    right: 26px;
    top: 26px;
    z-index: 1;
    :deep(.el-tag__content) {
      font-weight: bold;
      font-size: 14px;
    }
  }

  .task-body {
    flex: 1;
    position: relative;

    .task-sider {
      position: absolute;
      left: 36px;
      top: 26px;
      z-index: 1;
      display: flex;
      flex-wrap: nowrap;
      .sensor-item,
      .sau-item,
      .measure-item {
        margin-right: 23px;
        height: 50px;
        display: flex;
        align-items: center;
        cursor: pointer;
        padding: 0 12px 0 56px;
        box-shadow: 0px 3px 6px 1px rgba(0, 0, 0, 0.16);
        border-radius: 6px 6px 6px 6px;
        border: 1px solid #d7dbe4;
        font-size: 15px;
        color: #333;
      }

      .sensor-item {
        background: #ffffff url('@/assets/task/task01.png') 12px center / 32px 32px no-repeat;
      }

      .sau-item {
        background: #ffffff url('@/assets/task/task02.png') 12px center / 32px 32px no-repeat;
      }

      .measure-item {
        background: #ffffff url('@/assets/task/task03.png') 12px center / 32px 32px no-repeat;
      }
    }

    .task-content {
      flex: 1;
      min-width: 0;
      height: 100%;

      .drawflow {
        width: 100%;
        height: 100%;
      }
    }
  }

  :deep(.vue-flow__node-input) {
    width: auto;
    background: linear-gradient(180deg, #ecf9f9 0%, #ffffff 17%, #ffffff 100%);
    box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.16);
    border-radius: 8px 8px 8px 8px;
    border: 1px solid #dddfe6;
  }

  :deep(.vue-flow__node-default) {
    width: auto;
    padding: 0;
    box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.16);
    border-radius: 8px 8px 8px 8px;
    border: 1px solid #dddfe6;
  }

  :deep(.vue-flow__node-output) {
    width: auto;
    background: linear-gradient(180deg, #fff5ed 0%, #ffffff 13%, #ffffff 100%);
    box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.16);
    border-radius: 8px 8px 8px 8px;
    border: 1px solid #dddfe6;
  }

  // :deep(.vue-flow__edge.selected .vue-flow__edge-path) {
  //   stroke-width: 3;
  //   stroke: #d2970c !important;
  // }

  :deep(.vue-flow) {
    .vue-flow__handle {
      background-color: #1a40cb;
      height: 24px;
      width: 8px;
      border-radius: 4px;
    }
    .el-form-item {
      &.special-item {
        .el-form-item__label {
          color: #666;
        }
      }

      .el-form-item__label {
        color: #b8b8b8;
      }
    }
  }
}
</style>
